PostgreSQL 15ではpublicスキーマへの書き込みが制限されます
PostgreSQLではデータベースを作成すると、デフォルトで public スキーマが作成され、任意のユーザーがこのスキーマにオブジェクトを作成できました。
CVE-2018-1058 でpublic
スキーマのこの仕様とsearch_path
を使ったトロイの木馬攻撃の脆弱性(仕様の潜在リスク)が報告されました。
この攻撃から守るために、以下のような方法が推奨されています。
public
スキーマのCREATE
権限をREVOKE
- ユーザーごとにスキーマを割り振る
search_path
にpublic
スキーマが含まれないように調整
PostgreSQL 15からは、1つ目の回避策がデフォルトで有効になり、データベースのオーナーだけがpublic
スキーマにオブジェクトを作成できるようになります。
Remove PUBLIC creation permission on the public schema (Noah Misch)
This is a change in the default for newly-created databases in existing clusters and for new clusters;
USAGE
permissions on the public schema has not been changed. Databases restored from previous Postgres releases will be restored with their current permissions. Users wishing to have the old permissions on new objects will need to grantCREATE
permission forPUBLIC
on thepublic
schema; this change can be made ontemplate1
to cause all new databases to have these permissions.
PostgreSQL 15 のかなり大きなバージョン非互換。publicスキーマに対するCREATE,USAGE権限が無くなります。 https://t.co/OetbIJBdnC
— Noriyoshi Shinoda (@nori_shinoda) September 10, 2021
この仕様変更を動作確認します。
PostgreSQLのデータベース・スキーマ・テーブルの関係
最初にPostgreSQLのデータベースとスキーマとテーブルの関係をおさらいします。
initdb
を実行すると、データベースクラスターが作成され、デフォルトで
- template0
- template1
- postgres
の3つのデータベースが作成されます。
データベースは名前空間を分割でき、この粒度をスキーマと呼びます。
各データベースにはデフォルトで public
スキーマが作成されます。
ユーザーはテーブルを特定のスキーマに作成します。
CVE-2018-1058 とそのリスク軽減方法
この public
スキーマは /tmp
ディレクトリのようなもので、任意のユーザーがオブジェクト(例えばテーブル)を作成できます。
オブジェクトを検索するスキーマを指定する search_path
にpublic
スキーマが含まえていると、異なるスキーマに同じ名前のオブジェクトを作成可能で、「トロイの木馬」攻撃のリスクがあります(CVE-2018-1058)。
public スキーマをセキュアに運用するための手順は公式ドキュメントで「セキュアなスキーマの使用パターン」として解説されており
public
スキーマのCREATE
権限を REVOKE- ユーザーごとにスキーマを割り振る
search_path
に public スキーマが含まれないように調整
などが推奨されます。
PostgreSQL 15ではデータベースのオーナーだけがpublicスキーマに書き込める
PostgreSQL 15では、public
スキーマの権限は REVOKE CREATE ON SCHEMA public FROM PUBLIC
を実行した状態となり、データベースのオーナー(pg_database_owner
)だけがオブジェクトを作成できます。
この動作を確認します。
別オーナーの public スキーマにはオブジェクトを作成できない
ユーザー john
を作成し、デフォルトの postgres データベースの public
スキーマにテーブルを作成してみましょう。
postgres=# create user john with password 'YOURPASSWORD'; CREATE ROLE postgres=# \c postgres john Password for user john: You are now connected to database "postgres" as user "john". postgres=> create table public.a(a int); ERROR: permission denied for schema public LINE 1: create table public.a(a int);
permission denied
が発生し、テーブルを作成できませんでした。
データベースオーナーのスキーマにはオブジェクトを作成できる
セキュアなスキーマの使用パターンでは、public スキーマを使用せず、ユーザーごとにスキーマを払い出すことが推奨されています。
ユーザー john 向けのスキーマを作成します。
postgres=# CREATE SCHEMA AUTHORIZATION john; CREATE SCHEMA postgres=# \dn List of schemas Name | Owner --------+------------------- public | pg_database_owner john | john (2 rows)
このスキーマには、テーブルを作成できます。
postgres=> create table john.a(a int); CREATE TABLE postgres=> \dt List of relations Schema | Name | Type | Owner --------+------+-------+---------- john | a | table | john (3 rows)
自分がオーナーの public スキーマにはオブジェクトを作成できる
次に、自身が所有するデータベース(test
)の public
スキーマにテーブルを作成してみましょう。
postgres=# create database test with owner john; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges -----------+----------+----------+------------+------------+------------+-----------------+----------------------- postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres + | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres + | | | | | | | postgres=CTc/postgres test | john | UTF8 | en_US.utf8 | en_US.utf8 | | libc | (4 rows) postgres=> \c test john You are now connected to database "test" as user "john".
この public スキーマにはテーブルを作成できます。
test=> create table public.a(a int); CREATE TABLE test=> \dt List of relations Schema | Name | Type | Owner --------+------+-------+---------- public | a | table | john (1 rows)
public スキーマに CREATE 権限を付与
諸般の事情により、public スキーマに CREATE 権限を付与したいケースがあるかもしれません。
そのような場合は、GRANT CREATE ON SCHEMA public TO PUBLIC;
を実行しましょう。
template1
データベースに対して実行すると、新規データベースの挙動を変更できます。
PostgreSQL 15にアップグレートすると既存仕様を踏襲
既存データベースを15にアップグレードすると、移行元のpublic
スキーマ権限が引き継がれます。
public スキーマの権限をデフォルトのままにしていた場合、セキュアなスキーマの使用パターンのガイドラインを確認の上、public スキーマをセキュアに活用しましょう。
まとめ
2022年内のリリースが予定されているPostgreSQL 15では、データベースのオーナー以外はpublicスキーマにオブジェクトを作成できなくなる予定です。
PostgreSQL 15で導入される、インパクトが大きな仕様変更の一つです。
PostgreSQL 15に移行後にアプリケーション・エラーが発生した場合、これを機にpublic
スキーマの 権限や search_path
と向き合い、セキュアなスキーマの使用パターンのガイドラインを参考に、public
スキーマをよりセキュアに活用しましょう。
それでは。
参考
- PostgreSQL: Documentation: 15: E.1. Release 15
- A Guide to CVE-2018-1058: Protect Your Search Path - PostgreSQL wiki
- Waiting for PostgreSQL 15 – Revoke PUBLIC CREATE from public schema, now owned by pg_database_owner. – select * from depesz;
- PostgreSQL: Documentation: 15: 5.9.6. Usage Patterns
- 篠田の虎の巻「PostgreSQL 15 Beta 1 新機能検証結果」